/**
 * auth: marion.lau
 * mail: marion.lau.z@gmail.com
 * lastChange: 2021/12/28 10:20
 */
const dataBase = {
  version: '1.0.0',
  auth: 'marion.lau',
  mail: 'marion.lau.z@gmail.com',
  root: {},
}

const hourToMS = 60 * 60 * 1000

const defaultExpiryTime = 24 * 1000 * hourToMS

// 使用cookie来模仿storage
const cookies = {
  /**
   * 写入cookie
   */
  setItem(name, value) {
    const dataStr = `${name}=${value}; expires=${new Date(
      Date.now() + defaultExpiryTime
    ).toUTCString()}`
    document.cookie = dataStr
  },

  /**
   * 从cookie中取出
   */
  getItem() {
    const cookies = document.cookie.split(';')
    return cookies.reduce((prev, curr) => {
      const [key, value] = curr.split('=')
      prev[key] = value
      return prev
    }, {})['dataRoot']
  },
}

/**
 * 检查当前环境是否支持storage
 */
function checkStorage() {
  const storage = window.localStorage

  if (!storage) return false

  try {
    const val = Math.random().toString(36)
    storage.setItem('checkStorage', val)
    const value = storage.getItem('checkStorage')
    storage.removeItem('checkStorage')
    return val === value
  } catch (err) {
    return false
  }
}
// 如果不支持store就使用cookie
let storage = checkStorage() ? window.localStorage : cookies

/**
 * 将用户数据写入缓存，
 */
function setStorage() {
  const dataStr = encodeURIComponent(JSON.stringify(dataBase))
  storage.setItem('dataRoot', dataStr)
}

/**
 * 从缓存中读取数据
 */
function getStorage() {
  const dataBase = storage.getItem('dataRoot')
  return dataBase ? JSON.parse(decodeURIComponent(dataBase)).root : {}
}

/**
 * 计算数据失效时间
 * @param expiryTime
 * @returns
 */
function getExpiryTime(expiryTime, name) {
  const now = Date.now()

  // 有效期大于当前时间，直接返回
  if (expiryTime > now) {
    return expiryTime
  }

  // 未传入有效期，返回900天后的当前时间（有效期约三年）
  if (!expiryTime) {
    return now + 900 * 24 * 60 * 60 * 1000
  }

  // 传入有效期，但有效期是秒数，转成毫秒
  if (expiryTime > 1e10 && expiryTime < now) {
    return expiryTime * 1000
  }

  // 传入了有效期，但有效期不符合规则，数据在关闭窗口后直接失效
  return now
}

/**
 * 校验数据有效性
 * @param expiryTime
 * @returns
 */
function dataValidate(expiryTime) {
  return Date.now() < expiryTime
}

/**
 * 复制数据
 * @param data
 * @returns
 */
function cloneData(data) {
  return typeof data === 'object' ? JSON.parse(JSON.stringify(data)) : data
}

/**
 * storage 管理类
 */
class StorageManage {
  constructor() {
    this.listenerCalls = {}
    dataBase.root = getStorage()
  }

  /**
   * 写入管理
   * @param name key 需要保存的属性
   * @param data data 需要保存的数据，不接受方法、正则等非正常数据类型
   * @param expiryTime timeSteamp 时间戳，数据到期时间，以秒为单位，如未传则该数据会保存约三年时间
   */
  set(name, data, expiryTime = defaultExpiryTime) {
    // 将状态加入到全局对象
    dataBase.root[name] = {
      expiryTime: getExpiryTime(expiryTime, name),
      val: data,
    }
    // 调用命令将数据写入缓存中
    setStorage()
    // 检查是否有对应的监听程序，如果有，调用监听程序
    const listenerCall = this.listenerCalls[name]
    if (listenerCall) {
      listenerCall.map(item => {
        return item(data)
      })
    }
    return true
  }
  /**
   * 读取管理
   * @param name
   * @returns
   */
  get(name) {
    const data = dataBase.root[name]
    // 避免数据中存在引用类型导致全局变量中的状态发生变化
    return dataValidate(data?.expiryTime) ? cloneData(data.val) : undefined
  }

  /**
   * 移除指定的数据
   */
  remove(name) {
    const result = Reflect.deleteProperty(dataBase.root, name)
    if (result) {
      setStorage()
      return true
    }
    return false
  }

  /**
   * 清除所有的数据
   */
  clear() {
    dataBase.root = {}
    setStorage()
    return true
  }

  /**
   * 监听storage的变化并通知订阅者
   * @param callback
   */
  listener(name, callback) {
    // 判断是否已经存在对指定字段的监听
    const hasCallback = Reflect.has(this.listenerCalls, name)
    // 有就push，没有则写入
    if (hasCallback) {
      this.listenerCalls[name].push(callback)
    } else {
      this.listenerCalls[name] = [callback]
    }
  }
}

export default new StorageManage()
